package com.wenzheng.mybatisplus.service.impl;

import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.core.conditions.*;
import com.baomidou.mybatisplus.core.mapper.*;
import com.baomidou.mybatisplus.core.metadata.*;
import com.wenzheng.mybatisplus.service.*;
import com.wenzheng.mybatisplus.util.*;
import lombok.AllArgsConstructor;
import org.apache.ibatis.session.*;
import org.mybatis.spring.*;
import org.springframework.transaction.annotation.*;
import org.springframework.util.*;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;


/**
 * @author tdf
 * @date 2019-09-16 16:51
 * 1:BaseBatchServiceImpl 是 通过反射封装的一层dao  ,丰富了mybatis的基础dao层
 * 2：支持基础的增删查改分页(crud page)等基础操作  ，支持批量插入。批量插入支持插入阈值
 * 3：更新/删除/根据主键查询 是根据反射获取主键进行对应的操作
 * 4：反射时根据mybatis的注解名称TableId来获取注解的属性名称的（因此要想使用本类，model层必须使用TableId 来注解主键（多个注解的情况下。按从上到下以第一个为准））
 * 5：使用方式
 * model
 **/
@AllArgsConstructor
public class BaseBatchServiceImpl<T extends BaseMapper<E>, E> implements BaseBatchService<E> {

    private final SqlSessionTemplate sqlSessionTemplate;
    /**
     * 实体类对象class
     */
    private Class<E> entityClass;
    /**
     * 默认保存/修改 提交大小
     */
    private static final int DEFAULT_BATCH_SIZE = 20;


    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void batchInsert(List<E> beanList) {
        batchInsert(beanList, DEFAULT_BATCH_SIZE);
    }


    /**
     * 分页查询
     *
     * @param pageModel :查询参数
     * @return IPage
     */
    @Override
    public PageUtils<E> page(PageModel<E> pageModel) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return new PageUtils<>(sqlSession.getMapper(getEntityMapperClass()).selectPage(pageModel.getPage(), pageModel.getQueryWrapper()));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 新增/修改
     *
     * @param entity :实体
     * @return Boolean
     */
    @Override
    public Boolean merge(E entity) {
        if (entity != null && Objects.nonNull(getPrimaryKeyValue(entity))) {
            return updateById(entity) > 0;
        }
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).insert(entity) > 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 新增
     *
     * @param entity :实体
     * @return E
     */
    @Override
    public E save(E entity) {
        insert(entity);
        return entity;
    }

    /**
     * 修改
     *
     * @param entity :实体
     * @return E
     */
    @Override

    public E update(E entity) {
        Assert.notNull(getPrimaryKeyValue(entity), "id不能为空");
        SqlSession sqlSession = null;
        Assert.notNull(selectById(getPrimaryKeyValue(entity).toString()), "没有查到主键为【" + getPrimaryKeyValue(entity) + "】的数据");
        try {

            E en = selectById(getPrimaryKeyValue(entity).toString());
            Assert.notNull(en, "没有查到主键为【" + getPrimaryKeyValue(entity) + "】的数据");
            sqlSession = getEntityMapper();
            sqlSession.getMapper(getEntityMapperClass()).updateById(BeanUtils.copyPropertiesIgnoreNull(entity, en));
            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    /**
     * 根据id 删除
     *
     * @param id :主键id
     * @return Boolean
     */
    @Override
    public Boolean deleteById(String id) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).deleteById(id) > 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据id 删除
     *
     * @param beanList :批量新增实体
     * @param size：    批量阈值
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void batchInsert(List<E> beanList, int size) {
        if (size < 1) {
            size = DEFAULT_BATCH_SIZE;
        }
        SqlSession session = null;
        try {
            session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
            BaseMapper<E> mapper = session.getMapper(getEntityMapperClass());

            int count = 0;
            for (E aBeanList : beanList) {
                mapper.insert(aBeanList);
                if (++count % size == 0) {
                    session.commit();
                    session.clearCache();
                }
            }
            session.commit();
            session.clearCache();
        } catch (Exception e) {
            e.printStackTrace();
            if (session != null) {
                session.rollback();
            }
            throw e;
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    /**
     * 批量修改
     *
     * @param beanList :批量修改实体
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void batchUpdate(List<E> beanList) {
        batchUpdate(beanList, DEFAULT_BATCH_SIZE);
    }


    /**
     * 批量修改
     *
     * @param beanList  :批量修改实体
     * @param batchSize :批量修改 大小
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public void batchUpdate(List<E> beanList, int batchSize) {
        SqlSession session = null;
        try {
            session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
            BaseMapper<E> mapper = session.getMapper(getEntityMapperClass());

            int count = 0;
            for (E aBeanList : beanList) {
                mapper.updateById(aBeanList);
                if (++count % batchSize == 0) {
                    session.commit();
                    session.clearCache();
                }
            }
            session.commit();
            session.clearCache();
        } catch (Exception e) {
            e.printStackTrace();
            if (session != null) {
                session.rollback();
            }
            throw e;
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }


    /**
     * 通过反射获取T实际的Class对象
     *
     * @return Class<T>
     */
    @SuppressWarnings("unchecked")
    private Class<T> getEntityMapperClass() {
        return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
     * 通过反射获取T实际的Class对象
     *
     * @return Class<E>
     */
    @SuppressWarnings("unchecked")
    private Class<E> getEntityClass() {
        if (null == entityClass) {
            entityClass = (Class<E>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        }
        return entityClass;
    }

    /**
     * SIMPLE 就是普通的执行器；REUSE 执行器会重用预处理语句
     * （prepared statements）； BATCH 执行器将重用语句并执行批量更新。
     * 通过反射获取T实际的Class对象
     *
     * @return :SqlSession
     */
    private SqlSession getEntityMapper() {
        return sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.SIMPLE, true);
    }

    /**
     * 插入
     *
     * @param entity ：
     * @return 插入的条数
     */
    @Override
    public int insert(E entity) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).insert(entity);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public int deleteById(Serializable id) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).deleteById(id);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public int deleteByMap(Map<String, Object> columnMap) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).deleteByMap(columnMap);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public int delete(Wrapper<E> wrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).delete(wrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public int deleteBatchIds(Collection<? extends Serializable> idList) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).deleteBatchIds(idList);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public int updateById(E entity) {
        SqlSession sqlSession = null;
        try {
            E en = selectById(getPrimaryKeyValue(entity).toString());
            Assert.notNull(en, "没有查到主键为【" + getPrimaryKeyValue(entity) + "】的数据");
            sqlSession = getEntityMapper();
            return sqlSession.getMapper(getEntityMapperClass()).updateById(BeanUtils.copyPropertiesIgnoreNull(entity, en));
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    @Override
    public int update(E entity, Wrapper<E> updateWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            E en = selectById(getPrimaryKeyValue(entity).toString());
            Assert.notNull(en, "没有查到主键为【" + getPrimaryKeyValue(entity) + "】的数据");
            return sqlSession.getMapper(getEntityMapperClass()).update(BeanUtils.copyPropertiesIgnoreNull(entity, en), updateWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    @Override
    public E selectById(Serializable id) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectById(id);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public List<E> selectBatchIds(Collection<? extends Serializable> idList) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectBatchIds(idList);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public List<E> selectByMap(Map<String, Object> columnMap) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectByMap(columnMap);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public E selectOne(Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectOne(queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Integer selectCount(Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectCount(queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public List<E> selectList(Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectList(queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public List<Map<String, Object>> selectMaps(Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectMaps(queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public List<Object> selectObjs(Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectObjs(queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    @Deprecated
    public IPage<E> selectPage(IPage<E> page, Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectPage(page, queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public PageUtils<E> page(IPage<E> page, Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return new PageUtils<>(sqlSession.getMapper(getEntityMapperClass()).selectPage(page, queryWrapper));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    @Override
    public PageUtils<E> selectPage(PageModel<E> pageModel) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return new PageUtils<>(sqlSession.getMapper(getEntityMapperClass()).selectPage(pageModel.getPage(), pageModel.getQueryWrapper()));
        }
    }

    /**
     * 批量更新&插入，有主键时更新，没主键时插入  IPage<T></>
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void mergeList(List<E> beanList) {
        List<E> update = new ArrayList<>(beanList.size());
        List<E> insert = new ArrayList<>(beanList.size());
        for (E entity : beanList) {
            if (entity != null && Objects.nonNull(getPrimaryKeyValue(entity))) {
                update.add(entity);
            } else {
                insert.add(entity);
            }
        }
        if (!update.isEmpty()) {
            batchUpdate(update);
        }
        if (!insert.isEmpty()) {
            batchInsert(insert);
        }
    }

    /**
     * 由于前台分页数据格式的要求，这里不建议使用
     */
    @Override
    @Deprecated
    public IPage<Map<String, Object>> selectMapsPage(IPage<E> page, Wrapper<E> queryWrapper) {
        try (SqlSession sqlSession = getEntityMapper()) {
            return sqlSession.getMapper(getEntityMapperClass()).selectMapsPage(page, queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取实体对象的id
     *
     * @return Object：主键
     */
    private String getPrimaryKeyName() {
        Field[] fields = getEntityClass().getDeclaredFields();
        for (Field field : fields) {
            //是否被特定注解修饰
            if (field.isAnnotationPresent(TableId.class)) {
                return field.getName();
            }
        }
        throw new IllegalArgumentException("没有发现被【TableId】 修饰的主键");
    }

    /**
     * 获取实体对主键的value
     *
     * @return Object：主键
     */
    private Object getPrimaryKeyValue(E e) {
        Assert.notNull(e, "实体不能为空");
        try {
            return getEntityClass().getDeclaredMethod(getGetter(getPrimaryKeyName())).invoke(e);
        } catch (Exception exception) {
            exception.printStackTrace();
            throw new IllegalArgumentException("获取实体对象的id");
        }
    }

    private static String getGetter(String fieldName) {
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

}
